package com.itheima.health.utils;

import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.serialize.SerializableSerializer;

import java.sql.Statement;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * @author YZY
 * @version v1.0
 * @Date 2020/4/15
 * @Description zookeeper分布式锁
 */
public class ZookeeperLock implements Lock {
    private static final String LOCK_PATH = "/LOCK";
    private static final String ZOOKEEPER_IP_PORT = "localhost:2181";
    //创建重试策略
    private ZkClient client = new ZkClient(ZOOKEEPER_IP_PORT, 1000, 1000, new SerializableSerializer());

    private static Logger logger = LoggerFactory.getLogger(ZookeeperLock.class);

    private CountDownLatch cdl;//倒时器
    private String beforePath;//前一个节点
    private String currentPath;//当前请求的节点

    //判断zookeeper中是否存在lock节点,没有则创建
    public ZookeeperLock() {
        if (!this.client.exists(LOCK_PATH)) {
            this.client.createPersistent(LOCK_PATH);
        }
    }


    //阻塞式加锁
    @Override
    public void lock() {
        Statement sts = null;
        //尝试拿锁
        if (tryLock()) {
            return;
        }
        //如果没拿到锁,进行阻塞,等待通知
        waitForLock();
        //递归调用当前锁
        lock();
    }


    private void waitForLock() {
        IZkDataListener listener = new IZkDataListener() {
            @Override
            public void handleDataChange(String s, Object o) throws Exception {

            }

            //监控当前置节点被删除时
            @Override
            public void handleDataDeleted(String s) throws Exception {
                if (cdl != null) {
                    cdl.countDown();
                }
            }
        };
        //给前一个节点 增加一个监听器
        this.client.subscribeDataChanges(beforePath, listener);

        if (this.client.exists(beforePath)) {
            cdl = new CountDownLatch(1);
            try {
                cdl.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.client.unsubscribeDataChanges(beforePath, listener);
    }

    //非阻塞式加锁
    public boolean tryLock() {
        //如果currentpath为空,则为第一次加锁,第一次加锁赋值currentPath
        if (currentPath == null || currentPath.length() <= 0) {
            currentPath = this.client.createEphemeralSequential(LOCK_PATH + '/', "lock");
        }
        //获取所有临时节点 并排序
        List<String> childrens = this.client.getChildren(LOCK_PATH);
        Collections.sort(childrens);

        if (currentPath.equals((LOCK_PATH + '/' + childrens.get(0)))) {
            //如果当前节点与集合中第一个节点相同,则直接拿锁
            return true;
        } else {
            int wz = Collections.binarySearch(childrens, currentPath.substring(6));
            beforePath = LOCK_PATH + '/' + childrens.get(wz - 1);
        }
        return false;
    }


    public void unlock() {
        //删除当前临时节点
        client.delete(currentPath);
    }






    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    @Override
    public Condition newCondition() {
        return null;
    }
}

